package com.stars.easyms.datasource.properties;

import com.stars.easyms.datasource.enums.DatabaseType;
import com.stars.easyms.datasource.exception.IllegalDataSourcePropertiesException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.Nullable;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * <p>className: EasyMsDataSourcePropertiesProcessor</p>
 * <p>description: 数据源属性处理者</p>
 *
 * @author guoguifang
 * @version 1.1.0
 * @date 2019-02-28 15:07
 */
@Slf4j
public final class EasyMsDataSourcePropertiesProcessor {

    private String dataSourceName;

    private String dataSourcePrefix;

    private final Properties masterProperties = new Properties();

    public EasyMsDataSourcePropertiesProcessor(String dataSourceName, boolean isDefault) {
        this.dataSourceName = dataSourceName;
        this.dataSourcePrefix = EasyMsDataSourceProperties.DATASOURCE_NAME_PREFIX + (isDefault ? "." : "-" + dataSourceName + ".");
    }

    public Properties getProperties(DatabaseType databaseType, boolean isMaster) {
        // 创建默认属性
        Map<String, Object> defaultPropertiesValueMap = new HashMap<>(32);
        defaultPropertiesValueMap.put("minIdle", 5);
        defaultPropertiesValueMap.put("initialSize", 5);
        defaultPropertiesValueMap.put("maxActive", 300);
        defaultPropertiesValueMap.put("timeBetweenEvictionRunsMillis", 60000L);
        defaultPropertiesValueMap.put("minEvictableIdleTimeMillis", 300000L);
        defaultPropertiesValueMap.put("validationQuery", databaseType.getDefaultValidationQuery());
        defaultPropertiesValueMap.put("validationQueryTimeout", 1);
        defaultPropertiesValueMap.put("testWhileIdle", true);
        defaultPropertiesValueMap.put("testOnBorrow", false);
        defaultPropertiesValueMap.put("testOnReturn", false);
        defaultPropertiesValueMap.put("poolPreparedStatements", true);
        defaultPropertiesValueMap.put("maxPoolPreparedStatementPerConnectionSize", 20);
        defaultPropertiesValueMap.put("filters", "stat");
        defaultPropertiesValueMap.put("keepAlive", true);
        defaultPropertiesValueMap.put("killWhenSocketReadTimeout", false);
        defaultPropertiesValueMap.put("maxWait", 60000L);
        defaultPropertiesValueMap.put("useUnfairLock", false);
        defaultPropertiesValueMap.put("connectionProperties", "");
        defaultPropertiesValueMap.put("useGlobalDataSourceStat", false);

        // 获取确定属性
        Properties properties = new Properties();
        if (isMaster) {
            defaultPropertiesValueMap.forEach((key, value) -> {
                String propertiesValue = value.toString();
                try {
                    propertiesValue = getPropertiesValue(key, value, value.getClass(), true);
                } catch (Exception e) {
                    log.error("Get master datasource '{}' key '{}' fail, use default value '{}'!", dataSourceName, key, propertiesValue, e);
                }
                this.masterProperties.setProperty(key, propertiesValue);
                if (StringUtils.isNotBlank(propertiesValue)) {
                    properties.setProperty("druid." + key, propertiesValue);
                }
            });
        } else {
            defaultPropertiesValueMap.forEach((key, value) -> {
                String propertiesValue = this.masterProperties.getProperty(key);
                try {
                    propertiesValue = getPropertiesValue(key, propertiesValue, value.getClass(), false);
                } catch (Exception e) {
                    log.error("Get slave datasource '{}' key '{}' fail, use default value '{}'!", dataSourceName, key, propertiesValue, e);
                }
                if (StringUtils.isNotBlank(propertiesValue)) {
                    properties.setProperty("druid." + key, propertiesValue);
                }
            });
        }
        return properties;
    }

    @Nullable
    public String getMasterPropertiesValue(String key) {
        return getPropertiesValue(key, true);
    }

    public String getSlavePropertiesValue(String key) {
        return getPropertiesValue(key, false);
    }

    public String getPropertiesValue(String key, boolean isMaster, String defaultStr) {
        String value = getPropertiesValue(key, isMaster);
        return value != null ? value : defaultStr;
    }

    @Nullable
    private String getPropertiesValue(String key, boolean isMaster) {
        if (isMaster) {
            String value = BasePropertiesProcessor.getPropertyValue(dataSourcePrefix + key);
            if (StringUtils.isBlank(value)) {
                value = BasePropertiesProcessor.getPropertyValue(dataSourcePrefix + EasyMsMasterDataSourceProperties.DATASOURCE_MASTER_PREFIX + "." + key);
            }
            return value;
        }
        return BasePropertiesProcessor.getPropertyValue(dataSourcePrefix + EasyMsSlaveDataSourceProperties.DATASOURCE_SLAVE_PREFIX + "." + key);
    }

    private String getPropertiesValue(String key, Object defaultValue, Class<?> clazz, boolean isMaster) {
        String value = getPropertiesValue(key, isMaster);
        if (StringUtils.isBlank(value)) {
            return defaultValue == null ? null : String.valueOf(defaultValue);
        }
        Object checkObj = null;
        try {
            if (Integer.class.equals(clazz)) {
                checkObj = Integer.valueOf(value);
            } else if (Long.class.equals(clazz)) {
                checkObj = Long.valueOf(value);
            } else if (Boolean.class.equals(clazz)) {
                checkObj = Boolean.valueOf(value);
            }
        } catch (Exception e) {
            throw new IllegalDataSourcePropertiesException("{} datasource '{}' key '{}' value '{}' cannot be cast to {}!",
                    isMaster ? "Master" : "Slave", dataSourceName, key, value, clazz.getName());
        }
        if (!String.class.equals(clazz) && checkObj == null) {
            throw new IllegalDataSourcePropertiesException("{} datasource '{}' key '{}' value '{}' cannot be cast to {}!",
                    isMaster ? "Master" : "Slave", dataSourceName, key, value, clazz.getName());
        }
        return value;
    }

}